package net.cyue.simple.container.injector;

import net.cyue.simple.container.annotation.Inject;
import net.cyue.simple.container.data.InstanceWrapper;
import net.cyue.simple.container.data.MetaType;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;

public class DefaultInstanceRegistry extends AbstractInstanceRegistry {

    public DefaultInstanceRegistry() {
        super();
    }

    /**
     * 根据给定的 MetaType 创建对应的实例，并封装到 InstanceWrapper 中。
     *
     * @param metaType 包含待创建实例信息的 MetaType 对象，其中包含了类的类型信息。
     * @return 创建好的 InstanceWrapper 对象，其中包含了实例化的对象。
     */
    private Object createInstance(MetaType metaType) {
        // metaType 可能为 InjectableMetaType
        MetaType mt = new MetaType(metaType.getClz());
        // 实例已存在，直接返回
        if (instanceWrapperMap.containsKey(mt)) {
            return this.getInstanceWrapper(mt);
        }

        // 获取类的所有构造函数
        Class<?> clz = metaType.getClz();
        Constructor<?>[] constructorArray = clz.getDeclaredConstructors();
        // 如果类没有构造函数，则抛出异常
        if (constructorArray.length == 0) {
            throw new RuntimeException("instance 必须有构造函数");
        }

        // 取第一个构造函数，并设置其可访问
        Constructor<?> constructor = constructorArray[0];
        constructor.setAccessible(true);
        // 获取构造函数的参数类型
        Parameter[] parameters = constructor.getParameters();
        Class<?>[] parameterTypes = constructor.getParameterTypes();

        // 为构造函数的每个参数准备实例
        List<Object> constructParams = new ArrayList<>();
        for (int i = 0; i < parameters.length; i++) {
            Parameter p = parameters[i];
            Class<?> pClz = parameterTypes[i];
            Inject inject = p.getAnnotation(Inject.class);
            if (inject == null) {
                // 拿不到 injectable 对象实例
                Object instance = this.getInstance(pClz);
                if (instance != null) {
                    constructParams.add(instance);
                }
                continue;
            }

            Object instance = this.getInjectableInstance(inject);
            if (instance != null) {
                constructParams.add(instance);
            }
        }

        // 创建对象实例并初始化对象字段
        Object instance = null;
        try {
            // 使用准备好的参数实例化对象
            instance = constructor.newInstance(constructParams.toArray());
            // 给对象的空字段赋值
            this.injector.doInject(instance);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }

        // 创建并返回 InstanceWrapper 实例
        return instance;
    }

    @Override
    public InstanceWrapper registerInstance(MetaType metaType) {
        return this.registerInstance(metaType, this.createInstance(metaType));
    }

    /**
     * 注册一个实例到实例映射表中。
     * 此方法首先会递归检查并注册该实例的所有依赖项，然后为该实例创建一个实例包装器，并将其添加到映射表中。
     *
     * @param metaType 要注册的实例的元类型，包含实例的类信息和依赖信息。
     */
    @Override
    public InstanceWrapper registerInstance(MetaType metaType, Object instance) {
        // 判断是否存在
        if (this.instanceWrapperMap.containsKey(metaType)) {
            return this.instanceWrapperMap.get(metaType);
        }

        List<MetaType> dependencies = metaType.getDependencies();
        for (MetaType dependency : dependencies) {
            // 如果依赖项已经存在于映射表中，则跳过此次循环
            if (this.instanceWrapperMap.containsKey(dependency)) {
                continue;
            }

            // 递归注册依赖项
            this.registerInstance(dependency);
        }

        // 为当前实例创建实例包装器
        if (!metaType.getClz().isAssignableFrom(instance.getClass())) {
            System.out.println(metaType.getClz());
            System.out.println(instance.getClass());
            throw new IllegalArgumentException("instance class is not assignable to metaType");
        }
        InstanceWrapper wrapper = new InstanceWrapper(
            metaType,
            metaType.getName(),
            instance
        );
        // 将当前实例的实例包装器添加到映射表中
        this.instanceWrapperMap.put(
            metaType,
            wrapper
        );
        return wrapper;
    }
}

